TsoukMon Viewer: Pokemon, Moves, and Abilities

Pokemon

Pokemon

Moves

Moves

Abilities

Abilities

# Pokemon Summary
poke_filter <- filter(poke_db, Name == "Geodude")
#poke_filter

#################
# Base Stats
poke_filter_base <- select(poke_filter, Name, Types, HP, ATTACK, DEFENSE, SPECIAL_ATTACK, SPECIAL_DEFENSE, SPEED, Abilities, HiddenAbilities, Evolutions)

selected_poke_stats <- gather(poke_filter_base, key = "Pokemon_ID", value = "Stat")
colnames(selected_poke_stats) <- c("Stat","Value")

#################
# Ability
ability_filter <- select(poke_filter,Pokemon_ID,Abilities,HiddenAbilities)
ability_filter <- ability_filter %>% separate(Abilities, into = c("Ability1", "Ability2"), sep = ",", fill = "right")
ability_filter <- ability_filter %>% separate(HiddenAbilities, into = c("HiddenAbility1","HiddenAbility2","HiddenAbility3"), sep = ",", fill = "right")
ability_end <- gather(ability_filter, key="Pokemon_ID", value = "Ability", na.rm = TRUE)

ability_list <- left_join(ability_end, ability_db, by = c("Ability" = "Ability_ID"))
colnames(ability_list) <- c(" ","Ability_ID","Ability","Description","Flags")

selected_poke_abilities <- select(ability_list, Ability, Description, " ")
#ability_summary

#################
# Selected Pokemon Moves
# Split learned moves into vector
elements <- unlist(strsplit(poke_filter$Moves, ","))
# Extract levels and moves (odd vs even indexed values)
levels <- as.integer(elements[c(TRUE, FALSE)]) # Odd-indexed values
moves <- elements[c(FALSE, TRUE)]              # Even-indexed values

# Create the data frame
moves_df <- data.frame(Level = levels, Move = moves, stringsAsFactors = FALSE)
selected_poke_moves <- left_join(moves_df, move_db, by = c("Move" = "Move_ID"))[,1:8]
# Pokemon Summary
#poke_filter <- filter(poke_db, Name == "Geodude")
poke_filter <- poke_db

###################
#### Base Stats ###
###################
poke_filter_base <- select(poke_filter, Pokemon_ID, Name, FormName, Types, HP, ATTACK, DEFENSE, SPECIAL_ATTACK, SPECIAL_DEFENSE, SPEED, Abilities, HiddenAbilities, Evolutions, Moves)

selected_poke_stats <- pivot_longer(poke_filter_base
             ,cols = c("Types","HP","ATTACK","DEFENSE","SPECIAL_ATTACK","SPECIAL_DEFENSE","SPEED","Moves","Evolutions","Abilities","HiddenAbilities")
             ,names_to = "Stat"
             ,values_to = "Value"
             )

selected_poke_stats <- filter(selected_poke_stats,! Stat %in% c("Moves", "Abilities", "HiddenAbilities", "Evolutions"))
names(selected_poke_stats)[names(selected_poke_stats) == 'Name'] <- 'Pokemon'
#colnames(selected_poke_stats) <- c("Stat","Value")
#selected_poke_stats

###################
##### Ability ####
##################
ability_filter <- select(poke_filter,Pokemon_ID,Name,FormName,Abilities,HiddenAbilities)
ability_filter <- ability_filter %>% separate(Abilities, into = c("Ability1", "Ability2"), sep = ",", fill = "right")
ability_filter <- ability_filter %>% separate(HiddenAbilities, into = c("HiddenAbility1","HiddenAbility2","HiddenAbility3"), sep = ",", fill = "right")
#ability_end <- gather(ability_filter, key="Pokemon_ID", value = "Ability", na.rm = TRUE)

ability_end <- pivot_longer(ability_filter
             ,cols = c("Ability1","Ability2","HiddenAbility1","HiddenAbility2","HiddenAbility3")
             ,names_to = "Type"
             ,values_to = "Ability")

ability_list <- filter(left_join(ability_end, ability_db, by = c("Ability" = "Ability_ID")), !is.na(Ability))
colnames(ability_list) <- c("Pokemon_ID","Pokemon","FormName"," ","Ability","Name","Description","Flags")

selected_poke_abilities <- select(ability_list, Pokemon, Ability, Description, " ")
#ability_summary

###############################
### Selected Pokemon Moves ###
##############################
poke_filter_NA_RM <- filter(poke_filter, !is.na(Moves))
poke_filter_moves <- select(poke_filter_NA_RM, Pokemon_ID, Name, Moves)

# Step 1: Split the Moves column into separate rows based on commas
# Transform the data
poke_move_summary <- poke_filter_NA_RM %>%
  # Separate each value in the Moves column into individual rows
  separate_rows(Moves, sep = ",") %>%
  # Add a column with row numbers to distinguish between level and move names
  group_by(Name) %>%
  mutate(row_id = 2 * ceiling(row_number() / 2)) %>%
  # Use modulo to identify odd rows as move levels and even rows as move names
  mutate(category = ifelse(row_number() %% 2 == 1, "Move_Level", "Move_Name")) %>%
  # Pivot data so that Move_Level and Move_Name are in separate columns
  pivot_wider(names_from = category, values_from = Moves) %>%
  # Remove the row_id column and ungroup
  select(-row_id) %>%
  ungroup() %>%
  # Convert the Move_Level column to numeric (since it's character after splitting)
  mutate(Move_Level = as.integer(Move_Level)) %>%
  # Select relevant columns
  select(Pokemon_ID, Name, Move_Level, Move_Name)

# Remove duplicates
poke_move_summary <- unique(poke_move_summary)

selected_poke_moves <- left_join(poke_move_summary, move_db, by = c("Move_Name" = "Move_ID"))[,1:11]
colnames(selected_poke_moves) <- c("Pokemon_ID","Pokemon","Level","Move_ID","Name","Type","Category","Power","Accuracy","PP","Target")
filter(selected_poke_moves, Pokemon == "Tsoukinator")


filter(poke_move_summary, Name == "Abomasnow")
filter(poke_move_summary, Name == "Tsoukinator")

filter(poke_filter_NA_RM, Name == "Tsoukinator")
test <- data.frame(Name = poke_filter_NA_RM$Name, Moves = poke_filter_NA_RM$Moves)

tidy_moves <- test %>%
  # Separate each value in the Moves column into individual rows
  separate_rows(Moves, sep = ",") %>%
  # Add a column with row numbers to distinguish between level and move names
  group_by(Name) %>%
  mutate(row_id = 2 * ceiling(row_number()/2)) %>%
  # Use modulo to identify the odd rows as move levels and even rows as move names
  mutate(category = ifelse(row_id %% 2 == 1, "Move_Level", "Move_Name")) %>%
  # Pivot data so that Move_Level and Move_Name are in separate columns
  pivot_wider(names_from = category, values_from = Moves) %>%
  # Remove the row_id column and ungroup
  #select(-row_id) %>%
  ungroup()

tsouk <- filter(tidy_moves, Name == "Tsoukinator")

#2 * round(df$a/2)
t <- unlist(tsouk$Move_Name[[1]])
t
# Assuming you've already transformed the data as described
tidy_moves <- poke_filter_NA_RM %>%
  # Separate each value in the Moves column into individual rows
  separate_rows(Moves, sep = ",") %>%
  # Add a column with row numbers to distinguish between level and move names
  group_by(Name) %>%
  mutate(row_id = 2 * ceiling(row_number() / 2)) %>%
  # Use modulo to identify odd rows as move levels and even rows as move names
  mutate(category = ifelse(row_number() %% 2 == 1, "Move_Level", "Move_Name")) %>%
  # Pivot data so that Move_Level and Move_Name are in separate columns
  pivot_wider(names_from = category, values_from = Moves) %>%
  # Remove the row_id column and ungroup
  select(-row_id) %>%
  ungroup() %>%
  # Convert the Move_Level column to numeric (since it's character after splitting)
  mutate(Move_Level = as.integer(Move_Level)) %>%
  # Select relevant columns
  select(Pokemon_ID, Name, Move_Level, Move_Name)

# View the result
print(tidy_moves)


filter(tidy_moves, Name == "Tsoukinator")
tsoukmon_pkmn <- select(filter(readxl::read_excel("PokeDB.xlsx", sheet = "PkmnStats"), is.na(`Pokedex Number`)), Name)
tsoukmon_pkmn$Pokemon_ID <- toupper(gsub(" ","",tsoukmon_pkmn$Name))

tsoukmon_moves <- select(filter(readxl::read_excel("PokeDB.xlsx", sheet = "Moves")), Name)
tsoukmon_moves$Move_ID <- toupper(gsub(" ","",tsoukmon_moves$Name))

tsoukmon_abilities <- select(filter(readxl::read_excel("PokeDB.xlsx", sheet = "Abilities")), Name)
tsoukmon_abilities$Ability_ID <- toupper(gsub(" ","",tsoukmon_abilities$Name))

test <- filter(poke_db_short, (Name == "Tsoukinator" | Name == "Pikachu"))

test_join <- left_join(test, tsoukmon_pkmn, by = c("Name" = "Name"))
test_join

test$TsoukMon_OG <- test$Name %in% tsoukmon_pkmn$Name
library(plotly)
library(RColorBrewer)

palette_blues <- colorRampPalette(colors =c("#F34444","#FF7F0F","#FFDD57","#A0E515","#00C2B8"))

tsouk_colours <- c("#F34444","#FF7F0F","#FFDD57","#A0E515","#00C2B8")
tsouk_palette <- brewer.pal(n=5, name="Dark2")
tsouk_palette <- tsouk_palette
#val

foodbaby <- filter(selected_poke_stats, Pokemon == "FoodBaby" & Stat != "Types")
foodbaby$Stat <- factor(foodbaby$Stat, levels = c("HP", "ATTACK", "DEFENSE", "SPECIAL_ATTACK", "SPECIAL_DEFENSE", "SPEED"))

foodbaby$Value <- as.numeric(foodbaby$Value)
fig <- plot_ly(x = foodbaby$Value, y = foodbaby$Stat, type = 'bar', orientation = 'h',
               marker = list(color = brewer.pal(6, "Dark2")),
               text = foodbaby$Value,       # Show the Value as text
               textposition = 'auto') %>%
  layout(
    xaxis = list(range = c(0, 200),showticklabels = FALSE),
    yaxis = list(autorange = "reversed"
    ),
    height = 300
  )
Warning: Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()
fig



#brewer.pal(6, "Dark2")
library(plotly)

# Your custom color palette
tsouk_colours <- c("#F34444", "#FF7F0F", "#FFDD57", "#A0E515", "#00C2B8")

# Define breaks and corresponding colors
breaks <- c(-1, 40, 60, 90, 120, 200)
colors <- tsouk_colours

# Function to assign colors based on breaks
assign_color <- function(value) {
  cut(value, breaks = breaks, labels = colors, include.lowest = TRUE)
}

# Apply the color assignment function
value_colors <- assign_color(foodbaby$Value)

# Create the Plotly figure
fig <- plot_ly(x = foodbaby$Value, y = foodbaby$Stat, type = 'bar', orientation = 'h',
               marker = list(color = value_colors), # Use discrete colors
               text = foodbaby$Value,       # Show the Value as text
               textposition = 'auto') %>%
  layout(
    xaxis = list(range = c(0, 200), showticklabels = FALSE),
    yaxis = list(autorange = "reversed"),
    height = 300
  )
Warning: Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()
fig
NA
NA
# Load necessary library
library(plotly)
library(grDevices)

# Original color palette
tsouk_colours <- c("#F34444", "#FF7F0F", "#FFDD57", "#A0E515", "#00C2B8")

# Function to create a color scale with intermediate colors
create_color_scale <- function(colors, n) {
  # Interpolate colors
  color_scale <- colorRampPalette(colors)(n)
  return(color_scale)
}

# Create a new color scale with 10 colors
expanded_colours <- create_color_scale(tsouk_colours, 8)

# Define breaks (one more than the number of colors)
breaks <- c(-1, 0, 20, 40, 60, 80, 100, 150, 200)
colors <- expanded_colours  # Use the expanded color palette

# Function to assign colors based on breaks
assign_color <- function(value) {
  cut(value, breaks = breaks, labels = colors, include.lowest = TRUE)
}

base_stats <- filter(selected_poke_stats, Pokemon == "Haydos" & Stat != "Types")
base_stats$Stat <- factor(base_stats$Stat, levels = c("HP", "ATTACK", "DEFENSE", "SPECIAL_ATTACK", "SPECIAL_DEFENSE", "SPEED"))

base_stats$Value <- as.numeric(base_stats$Value)

# Apply the color assignment function
value_colors <- assign_color(base_stats$Value)

# Create the Plotly figure
fig <- plot_ly(x = base_stats$Value, y = base_stats$Stat, type = 'bar', orientation = 'h',
               marker = list(color = value_colors), # Use discrete colors
               text = base_stats$Value,       # Show the Value as text
               textposition = 'auto') %>%
  layout(
    xaxis = list(range = c(0, 200), showticklabels = FALSE, showgrid = FALSE),
    yaxis = list(autorange = "reversed"),
    height = 270,
    plot_bgcolor  = "transparent",
           paper_bgcolor = "transparent"
  )
Warning: Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()
fig
NA
LS0tDQp0aXRsZTogIlRzb3VrTW9uIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KPHN0eWxlPg0KLm1haW4tY29udGFpbmVyIHsNCiAgbWF4LXdpZHRoOiAxMDAlICFpbXBvcnRhbnQ7DQp9DQo8L3N0eWxlPg0KDQpUc291a01vbiBWaWV3ZXI6IFBva2Vtb24sIE1vdmVzLCBhbmQgQWJpbGl0aWVzDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9DQojIGh0dHBzOi8vbWVkaXVtLmNvbS9AU3BvcnRTY2lEYXRhL2h0dHBzLW1lZGl1bS1jb20tY29sbGluc25laWwzMDYtaG93LXRvLWNyZWF0ZS1pbnRlcmFjdGl2ZS1yZXBvcnRzLXdpdGgtci1tYXJrZG93bi1wYXJ0LWktNGZhOWRmNDZjZDkNCg0KI3JtKGxpc3Q9bHMoKSkgIyBDbGVhciB3b3Jrc3BhY2UNCmxpYnJhcnkoZGF0YS50YWJsZSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHJsaXN0KQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KERUKQ0KDQojIERlZmluZSB0aGUgcGF0aCB0byB0aGUgZm9sZGVyIHdoZXJlIHlvdXIgZmlsZXMgYXJlIGxvY2F0ZWQNCmZpbGVfcGF0aCA8LSAiLi4vUEJTLyINCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgTU9WRSBMSVNUICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBMaXN0IGFsbCBmaWxlcyBpbiB0aGUgZm9sZGVyIHRoYXQgc3RhcnQgd2l0aCAicG9rZW1vbiIgLSBidXQgbm90IHBva2Vtb24gbWV0cmljcw0KbW92ZV9maWxlcyA8LSBsaXN0LmZpbGVzKHBhdGggPSBmaWxlX3BhdGgsIHBhdHRlcm4gPSAiXm1vdmVzIiwgZnVsbC5uYW1lcyA9IFRSVUUpDQoNCiMgUmVhZCBhbGwgdGhlIGZpbGVzIHVzaW5nIHJlYWRfZGVsaW0NCm1vdmVfZGF0YSA8LSBtYXAobW92ZV9maWxlcywgcmVhZF9kZWxpbSwgZGVsaW0gPSAiXG4iLCB0cmltX3dzID0gVFJVRSwgY29sX25hbWVzID0gRkFMU0UpDQoNCiMgQ29tYmluZSB0aGUgbGlzdCBvZiBkYXRhIGZyYW1lcyBpbnRvIG9uZSBkYXRhIGZyYW1lIChpZiBhcHBsaWNhYmxlKQ0KbW92ZWxpc3RfdHh0IDwtIGJpbmRfcm93cyhtb3ZlX2RhdGEpDQoNCiMgU3BsaXQgb24gRXF1YWxzIHNpZ24NCm1vdmVsaXN0X3NwbGl0IDwtIGRhdGEuZnJhbWUoZG8uY2FsbCgncmJpbmQnLCBzdHJzcGxpdChhcy5jaGFyYWN0ZXIobW92ZWxpc3RfdHh0JFgxKSwnPScsZml4ZWQ9VFJVRSkpKQ0KbW92ZWxpc3Rfc3BsaXQkWDEgPC0gdHJpbXdzKG1vdmVsaXN0X3NwbGl0JFgxKQ0KbW92ZWxpc3Rfc3BsaXQkWDIgPC0gdHJpbXdzKG1vdmVsaXN0X3NwbGl0JFgyKQ0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMjIEFkZCBNb3ZlIE5hbWUgdG8gTW92ZXMNCm1vdmVsaXN0X3NwbGl0X2VuZCA8LSBtb3ZlbGlzdF9zcGxpdCAlPiUNCiAgbXV0YXRlKE1vdmVfSUQgPSBpZmVsc2UoZ3JlcGwoIl5cXFsiLCBYMSksIFgxLCBOQSkpICU+JSAgIyBFeHRyYWN0IHJvd3Mgd2hlcmUgWDEgc3RhcnRzIHdpdGggJ1snDQogIGZpbGwoTW92ZV9JRCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgRmlsbCBkb3duIHRoZSBncm91cCBuYW1lDQoNCiMgUmVtb3ZlIEtleSBGaWVsZHMgZnJvbSBkYXRhLWZyYW1lDQptb3ZlbGlzdF9zcGxpdF9lbmQgPC0gZmlsdGVyKG1vdmVsaXN0X3NwbGl0X2VuZCwgIWdyZXBsKCJeXFxbIiwgWDEpICYgIWdyZXBsKCJeXFwjLS0tIiwgWDEpICYgIWdyZXBsKCJeXFwjIiwgWDEpKQ0KDQojIEV4cGxpY2l0bHkgc3BlY2lmeWluZyB0aGUga2V5IGNvbHVtbiAnTW92ZV9OYW1lJw0KbW92ZV9kYiA8LSBwaXZvdF93aWRlcigNCiAgZGF0YSA9IG1vdmVsaXN0X3NwbGl0X2VuZCwNCiAgbmFtZXNfZnJvbSA9IFgxLCAgIyBDb2x1bW4gd2hvc2UgdmFsdWVzIGJlY29tZSBjb2x1bW4gbmFtZXMNCiAgdmFsdWVzX2Zyb20gPSBYMiwgIyBDb2x1bW4gd2hvc2UgdmFsdWVzIGZpbGwgdGhlIHRhYmxlDQogIGlkX2NvbHMgPSBjKE1vdmVfSUQpICAjIEV4cGxpY2l0bHkgc3BlY2lmeWluZyB0aGUga2V5IGNvbHVtbg0KKQ0KDQojIFJlbW92ZSBzcXVhcmUgYnJhY2tldHMgZnJvbSBNb3ZlX0lEIGNvbHVtbg0KbW92ZV9kYiRNb3ZlX0lEIDwtIHN1YnN0cihtb3ZlX2RiJE1vdmVfSUQsIDIsIG5jaGFyKG1vdmVfZGIkTW92ZV9JRCkgLSAxKQ0KDQptb3ZlX2RiIDwtIG1vdmVfZGJbb3JkZXIobW92ZV9kYiRNb3ZlX0lEKSxdDQptb3ZlX2RiX3Nob3J0IDwtIG1vdmVfZGIgJT4lIHNlbGVjdChOYW1lLCBUeXBlLCBDYXRlZ29yeSwgUG93ZXIsIEFjY3VyYWN5LCBUb3RhbFBQLCBUYXJnZXQsIERlc2NyaXB0aW9uLCBFZmZlY3RDaGFuY2UsIFByaW9yaXR5KSANCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIFBva2Vtb24gTElTVCAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KIyBMaXN0IGFsbCBmaWxlcyBpbiB0aGUgZm9sZGVyIHRoYXQgc3RhcnQgd2l0aCAicG9rZW1vbiIgLSBidXQgbm90IHBva2Vtb24gbWV0cmljcw0KcG9rZW1vbl9maWxlcyA8LSBsaXN0LmZpbGVzKHBhdGggPSBmaWxlX3BhdGgsIHBhdHRlcm4gPSAiXnBva2Vtb24iLCBmdWxsLm5hbWVzID0gVFJVRSkNCiMgRmlsdGVyIG91dCBmaWxlcyB0aGF0IGNvbnRhaW4gIl9tZXRyaWNzIiBpbiB0aGUgbmFtZQ0KcG9rZW1vbl9maWxlcyA8LSBwb2tlbW9uX2ZpbGVzWyFncmVwbCgiX21ldHJpY3MiLCBwb2tlbW9uX2ZpbGVzKV0NCg0KIyBSZWFkIGFsbCB0aGUgZmlsZXMgdXNpbmcgcmVhZF9kZWxpbQ0KcG9rZW1vbl9kYXRhIDwtIG1hcChwb2tlbW9uX2ZpbGVzLCByZWFkX2RlbGltLCBkZWxpbSA9ICJcbiIsIHRyaW1fd3MgPSBUUlVFLCBjb2xfbmFtZXMgPSBGQUxTRSkNCg0KIyBDb21iaW5lIHRoZSBsaXN0IG9mIGRhdGEgZnJhbWVzIGludG8gb25lIGRhdGEgZnJhbWUgKGlmIGFwcGxpY2FibGUpDQpwb2tlbGlzdF90eHQgPC0gYmluZF9yb3dzKHBva2Vtb25fZGF0YSkNCg0KIyBTcGxpdCBvbiBFcXVhbHMgc2lnbg0KcG9rZWxpc3Rfc3BsaXQgPC0gZGF0YS5mcmFtZShkby5jYWxsKCdyYmluZCcsIHN0cnNwbGl0KGFzLmNoYXJhY3Rlcihwb2tlbGlzdF90eHQkWDEpLCc9JyxmaXhlZD1UUlVFKSkpDQpwb2tlbGlzdF9zcGxpdCRYMSA8LSB0cmltd3MocG9rZWxpc3Rfc3BsaXQkWDEpDQpwb2tlbGlzdF9zcGxpdCRYMiA8LSB0cmltd3MocG9rZWxpc3Rfc3BsaXQkWDIpDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMgQWRkIFBva2Vtb24gTmFtZSB0byBlYWNoIHJvdw0KcG9rZWxpc3Rfc3BsaXRfZW5kIDwtIHBva2VsaXN0X3NwbGl0ICU+JQ0KICBtdXRhdGUoUG9rZW1vbl9JRCA9IGlmZWxzZShncmVwbCgiXlxcWyIsIFgxKSwgWDEsIE5BKSkgJT4lICAjIEV4dHJhY3Qgcm93cyB3aGVyZSBYMSBzdGFydHMgd2l0aCAnWycNCiAgZmlsbChQb2tlbW9uX0lEKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBGaWxsIGRvd24gdGhlIGdyb3VwIG5hbWUNCg0KIyBBc3NpZ24gSUQgb3JkZXIgdG8gZXhpc3RpbmcgUG9rZW1vbiBMaXN0LCBza2lwcGluZyB0aGUgZmlyc3Qgcm93DQpzZXREVChwb2tlbGlzdF9zcGxpdF9lbmQpWy0xLCBQb2tlZGV4X05vIDo9IC5HUlAsIGJ5ID0gUG9rZW1vbl9JRF0NCg0KIyBSZW1vdmUgS2V5IEZpZWxkcyBmcm9tIGRhdGEtZnJhbWUNCnBva2VsaXN0X3NwbGl0X2VuZCA8LSBmaWx0ZXIocG9rZWxpc3Rfc3BsaXRfZW5kLCAhZ3JlcGwoIl5cXFsiLCBYMSkgJiAhZ3JlcGwoIl5cXCMtLS0iLCBYMSkgJiAhZ3JlcGwoIl5cXCMiLCBYMSkpDQoNCiMgU3VtbWFyaXplIG11bHRpcGxlIGtleSB2YWx1ZXMgYnkgY29uY2F0ZW5hdGluZyB0aGVtDQpwb2tlbGlzdF9zcGxpdF9lbmQgPC0gcG9rZWxpc3Rfc3BsaXRfZW5kICU+JQ0KICBncm91cF9ieShQb2tlZGV4X05vLCBQb2tlbW9uX0lELCBYMSkgJT4lDQogIHN1bW1hcmlzZShYMiA9IHN0cl9jKFgyLCBjb2xsYXBzZSA9ICIsICIpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQojIEV4cGxpY2l0bHkgc3BlY2lmeWluZyB0aGUga2V5IGNvbHVtbiAnTW92ZV9OYW1lJw0KcG9rZV9kYiA8LSBwaXZvdF93aWRlcigNCiAgZGF0YSA9IHBva2VsaXN0X3NwbGl0X2VuZCwNCiAgbmFtZXNfZnJvbSA9IFgxLCAgIyBDb2x1bW4gd2hvc2UgdmFsdWVzIGJlY29tZSBjb2x1bW4gbmFtZXMNCiAgdmFsdWVzX2Zyb20gPSBYMiwgIyBDb2x1bW4gd2hvc2UgdmFsdWVzIGZpbGwgdGhlIHRhYmxlDQogIGlkX2NvbHMgPSBjKFBva2VkZXhfTm8sIFBva2Vtb25fSUQpICAjIEV4cGxpY2l0bHkgc3BlY2lmeWluZyB0aGUga2V5IGNvbHVtbg0KKQ0KDQojIFJlbW92ZSBzcXVhcmUgYnJhY2tldHMgZnJvbSBNb3ZlX0lEIGNvbHVtbg0KcG9rZV9kYiRQb2tlbW9uX0lEIDwtIHN1YnN0cihwb2tlX2RiJFBva2Vtb25fSUQsIDIsIG5jaGFyKHBva2VfZGIkUG9rZW1vbl9JRCkgLSAxKQ0KIyBJZiBOYW1lIGlzIG5vdCBwcmVzZW50LCByZXBsYWNlIHdpdGggcHJvcGVyIGNhc2UgSUQgKHNwbGl0IHByaW9yIHRvIGNvbW1hKQ0KcG9rZV9kYiROYW1lIDwtIGlmZWxzZSgNCiAgaXMubmEocG9rZV9kYiROYW1lKSwgDQogIHN0cl90b190aXRsZShzYXBwbHkoc3RyX3NwbGl0KHBva2VfZGIkUG9rZW1vbl9JRCwgIiwiLCBzaW1wbGlmeSA9IFRSVUUpLCBmdW5jdGlvbih4KSB4WzFdKSksDQogIHBva2VfZGIkTmFtZQ0KKQ0KDQojIElmIEZvcm1OYW1lIGlzIG5vdCBwcmVzZW50LCByZXBsYWNlIHdpdGggIlN0YW5kYXJkIEZvcm0iDQpwb2tlX2RiJEZvcm1OYW1lIDwtIGlmZWxzZShpcy5uYShwb2tlX2RiJEZvcm1OYW1lKSwiU3RhbmRhcmQgRm9ybSIscG9rZV9kYiRGb3JtTmFtZSkNCiMgU3BsaXQgb3V0IGJhc2Ugc3RhdHMNCnBva2VfZGIgPC0gcG9rZV9kYiAlPiUNCiAgc2VwYXJhdGUoQmFzZVN0YXRzLCBpbnRvID0gYygiSFAiLCAiQVRUQUNLIiwiREVGRU5TRSIsIlNQRUVEIiwiU1BFQ0lBTF9BVFRBQ0siLCJTUEVDSUFMX0RFRkVOU0UiKSwgc2VwID0gIiwiLCBmaWxsID0gInJpZ2h0IikNCg0KcG9rZV9kYiA8LSBwb2tlX2RiW29yZGVyKHBva2VfZGIkUG9rZWRleF9ObyksXQ0KcG9rZV9kYl9zaG9ydCA8LSBwb2tlX2RiICU+JSBzZWxlY3QoUG9rZWRleF9ObywgTmFtZSwgRm9ybU5hbWUsIFR5cGVzLCBIUCwgQVRUQUNLLCBERUZFTlNFLCBTUEVDSUFMX0FUVEFDSywgU1BFQ0lBTF9ERUZFTlNFLCBTUEVFRCwgQWJpbGl0aWVzLCBIaWRkZW5BYmlsaXRpZXMsIEV2b2x1dGlvbnMpIA0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgQWJpbGl0aWVzIExJU1QgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCiMgTGlzdCBhbGwgZmlsZXMgaW4gdGhlIGZvbGRlciB0aGF0IHN0YXJ0IHdpdGggInBva2Vtb24iIC0gYnV0IG5vdCBwb2tlbW9uIG1ldHJpY3MNCmFiaWxpdHlfZmlsZXMgPC0gbGlzdC5maWxlcyhwYXRoID0gZmlsZV9wYXRoLCBwYXR0ZXJuID0gIl5hYmlsaXRpZXMiLCBmdWxsLm5hbWVzID0gVFJVRSkNCiMgRmlsdGVyIG91dCBmaWxlcyB0aGF0IGNvbnRhaW4gIl9tZXRyaWNzIiBpbiB0aGUgbmFtZQ0KYWJpbGl0eV9maWxlcyA8LSBhYmlsaXR5X2ZpbGVzWyFncmVwbCgiX0lubmF0ZSIsIGFiaWxpdHlfZmlsZXMpXQ0KDQojIFJlYWQgYWxsIHRoZSBmaWxlcyB1c2luZyByZWFkX2RlbGltDQphYmlsaXR5X2RhdGEgPC0gbWFwKGFiaWxpdHlfZmlsZXMsIHJlYWRfZGVsaW0sIGRlbGltID0gIlxuIiwgdHJpbV93cyA9IFRSVUUsIGNvbF9uYW1lcyA9IEZBTFNFKQ0KDQojIENvbWJpbmUgdGhlIGxpc3Qgb2YgZGF0YSBmcmFtZXMgaW50byBvbmUgZGF0YSBmcmFtZSAoaWYgYXBwbGljYWJsZSkNCmFiaWxpdHlsaXN0X3R4dCA8LSBiaW5kX3Jvd3MoYWJpbGl0eV9kYXRhKQ0KDQojIFNwbGl0IG9uIEVxdWFscyBzaWduDQphYmlsaXR5bGlzdF9zcGxpdCA8LSBkYXRhLmZyYW1lKGRvLmNhbGwoJ3JiaW5kJywgc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKGFiaWxpdHlsaXN0X3R4dCRYMSksJz0nLGZpeGVkPVRSVUUpKSkNCmFiaWxpdHlsaXN0X3NwbGl0JFgxIDwtIHRyaW13cyhhYmlsaXR5bGlzdF9zcGxpdCRYMSkNCmFiaWxpdHlsaXN0X3NwbGl0JFgyIDwtIHRyaW13cyhhYmlsaXR5bGlzdF9zcGxpdCRYMikNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIyBBZGQgTW92ZSBOYW1lIHRvIE1vdmVzDQphYmlsaXR5bGlzdF9zcGxpdF9lbmQgPC0gYWJpbGl0eWxpc3Rfc3BsaXQgJT4lDQogIG11dGF0ZShBYmlsaXR5X0lEID0gaWZlbHNlKGdyZXBsKCJeXFxbIiwgWDEpLCBYMSwgTkEpKSAlPiUgICMgRXh0cmFjdCByb3dzIHdoZXJlIFgxIHN0YXJ0cyB3aXRoICdbJw0KICBmaWxsKEFiaWxpdHlfSUQpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEZpbGwgZG93biB0aGUgZ3JvdXAgbmFtZQ0KDQojIFJlbW92ZSBLZXkgRmllbGRzIGZyb20gZGF0YS1mcmFtZQ0KYWJpbGl0eWxpc3Rfc3BsaXRfZW5kIDwtIGZpbHRlcihhYmlsaXR5bGlzdF9zcGxpdF9lbmQsICFncmVwbCgiXlxcWyIsIFgxKSAmICFncmVwbCgiXlxcIy0tLSIsIFgxKSAmICFncmVwbCgiXlxcIyIsIFgxKSkNCg0KIyBFeHBsaWNpdGx5IHNwZWNpZnlpbmcgdGhlIGtleSBjb2x1bW4gJ0FiaWxpdHlfSUQnDQphYmlsaXR5X2RiIDwtIHBpdm90X3dpZGVyKA0KICBkYXRhID0gYWJpbGl0eWxpc3Rfc3BsaXRfZW5kLA0KICBuYW1lc19mcm9tID0gWDEsICAjIENvbHVtbiB3aG9zZSB2YWx1ZXMgYmVjb21lIGNvbHVtbiBuYW1lcw0KICB2YWx1ZXNfZnJvbSA9IFgyLCAjIENvbHVtbiB3aG9zZSB2YWx1ZXMgZmlsbCB0aGUgdGFibGUNCiAgaWRfY29scyA9IGMoQWJpbGl0eV9JRCkgICMgRXhwbGljaXRseSBzcGVjaWZ5aW5nIHRoZSBrZXkgY29sdW1uDQopDQoNCiMgUmVtb3ZlIHNxdWFyZSBicmFja2V0cyBmcm9tIE1vdmVfSUQgY29sdW1uDQphYmlsaXR5X2RiJEFiaWxpdHlfSUQgPC0gc3Vic3RyKGFiaWxpdHlfZGIkQWJpbGl0eV9JRCwgMiwgbmNoYXIoYWJpbGl0eV9kYiRBYmlsaXR5X0lEKSAtIDEpDQoNCmFiaWxpdHlfZGIgPC0gYWJpbGl0eV9kYltvcmRlcihhYmlsaXR5X2RiJEFiaWxpdHlfSUQpLF0NCg0KI2FiaWxpdHlfZGJfc2hvcnQgPC0gYWJpbGl0eV9kYiAlPiUgc2VsZWN0KE5hbWUsIEZvcm1OYW1lLCBUeXBlLCBIUCwgQVRUQUNLLCBERUZFTlNFLCBTUEVDSUFMX0FUVEFDSywgU1BFQ0lBTF9ERUZFTlNFLCBTUEVFRCwgQWJpbGl0aWVzLCBIaWRkZW5BYmlsaXRpZXMsIEV2b2x1dGlvbnMpIA0KDQpgYGANCg0KIyMgey50YWJzZXR9DQoNCiMjIyBQb2tlbW9uDQoNClBva2Vtb24NCmBgYHtyLCBlY2hvID0gRkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpEVDo6ZGF0YXRhYmxlKHBva2VfZGJfc2hvcnQsIHJvd25hbWVzID0gRkFMU0UsIGZpbHRlciA9ICJ0b3AiLCBjbGFzcyA9ICdjZWxsLWJvcmRlciBzdHJpcGUnLCBleHRlbnNpb25zID0gJ0J1dHRvbnMnLCBvcHRpb25zID0gbGlzdChkb20gPSAnQmZydGlwJywgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpLCBzZWFyY2ggPSBsaXN0KHNlYXJjaCA9ICIiKSkpICAlPiUNCiAgRFQ6OmZvcm1hdFN0eWxlKGNvbHVtbnMgPSBjKDE6bmNvbChwb2tlX2RiKSksIGZvbnRTaXplID0gJzgwJScpDQpgYGANCg0KIyMjIE1vdmVzDQpNb3Zlcw0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCkRUOjpkYXRhdGFibGUobW92ZV9kYl9zaG9ydCwgcm93bmFtZXMgPSBGQUxTRSwgZmlsdGVyID0gInRvcCIsIGNsYXNzID0gJ2NlbGwtYm9yZGVyIHN0cmlwZScsIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0KGRvbSA9ICdCZnJ0aXAnLCBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JyksIHNlYXJjaCA9IGxpc3Qoc2VhcmNoID0gIiIpKSkgICU+JQ0KICBEVDo6Zm9ybWF0U3R5bGUoY29sdW1ucyA9IGMoMTpuY29sKG1vdmVfZGIpKSwgZm9udFNpemUgPSAnODAlJykNCg0KYGBgDQoNCiMjIyBBYmlsaXRpZXMNCkFiaWxpdGllcw0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCkRUOjpkYXRhdGFibGUoYWJpbGl0eV9kYiwgcm93bmFtZXMgPSBGQUxTRSwgZmlsdGVyID0gInRvcCIsIGNsYXNzID0gJ2NlbGwtYm9yZGVyIHN0cmlwZScsIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0KGRvbSA9ICdCZnJ0aXAnLCBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JyksIHNlYXJjaCA9IGxpc3Qoc2VhcmNoID0gIiIpKSkgICU+JQ0KICBEVDo6Zm9ybWF0U3R5bGUoY29sdW1ucyA9IGMoMTpuY29sKGFiaWxpdHlfZGIpKSwgZm9udFNpemUgPSAnODAlJykNCmBgYA0KDQoNCmBgYHtyfQ0KIyBQb2tlbW9uIFN1bW1hcnkNCnBva2VfZmlsdGVyIDwtIGZpbHRlcihwb2tlX2RiLCBOYW1lID09ICJHZW9kdWRlIikNCiNwb2tlX2ZpbHRlcg0KDQojIyMjIyMjIyMjIyMjIyMjIw0KIyBCYXNlIFN0YXRzDQpwb2tlX2ZpbHRlcl9iYXNlIDwtIHNlbGVjdChwb2tlX2ZpbHRlciwgTmFtZSwgVHlwZXMsIEhQLCBBVFRBQ0ssIERFRkVOU0UsIFNQRUNJQUxfQVRUQUNLLCBTUEVDSUFMX0RFRkVOU0UsIFNQRUVELCBBYmlsaXRpZXMsIEhpZGRlbkFiaWxpdGllcywgRXZvbHV0aW9ucykNCg0Kc2VsZWN0ZWRfcG9rZV9zdGF0cyA8LSBnYXRoZXIocG9rZV9maWx0ZXJfYmFzZSwga2V5ID0gIlBva2Vtb25fSUQiLCB2YWx1ZSA9ICJTdGF0IikNCmNvbG5hbWVzKHNlbGVjdGVkX3Bva2Vfc3RhdHMpIDwtIGMoIlN0YXQiLCJWYWx1ZSIpDQoNCiMjIyMjIyMjIyMjIyMjIyMjDQojIEFiaWxpdHkNCmFiaWxpdHlfZmlsdGVyIDwtIHNlbGVjdChwb2tlX2ZpbHRlcixQb2tlbW9uX0lELEFiaWxpdGllcyxIaWRkZW5BYmlsaXRpZXMpDQphYmlsaXR5X2ZpbHRlciA8LSBhYmlsaXR5X2ZpbHRlciAlPiUgc2VwYXJhdGUoQWJpbGl0aWVzLCBpbnRvID0gYygiQWJpbGl0eTEiLCAiQWJpbGl0eTIiKSwgc2VwID0gIiwiLCBmaWxsID0gInJpZ2h0IikNCmFiaWxpdHlfZmlsdGVyIDwtIGFiaWxpdHlfZmlsdGVyICU+JSBzZXBhcmF0ZShIaWRkZW5BYmlsaXRpZXMsIGludG8gPSBjKCJIaWRkZW5BYmlsaXR5MSIsIkhpZGRlbkFiaWxpdHkyIiwiSGlkZGVuQWJpbGl0eTMiKSwgc2VwID0gIiwiLCBmaWxsID0gInJpZ2h0IikNCmFiaWxpdHlfZW5kIDwtIGdhdGhlcihhYmlsaXR5X2ZpbHRlciwga2V5PSJQb2tlbW9uX0lEIiwgdmFsdWUgPSAiQWJpbGl0eSIsIG5hLnJtID0gVFJVRSkNCg0KYWJpbGl0eV9saXN0IDwtIGxlZnRfam9pbihhYmlsaXR5X2VuZCwgYWJpbGl0eV9kYiwgYnkgPSBjKCJBYmlsaXR5IiA9ICJBYmlsaXR5X0lEIikpDQpjb2xuYW1lcyhhYmlsaXR5X2xpc3QpIDwtIGMoIiAiLCJBYmlsaXR5X0lEIiwiQWJpbGl0eSIsIkRlc2NyaXB0aW9uIiwiRmxhZ3MiKQ0KDQpzZWxlY3RlZF9wb2tlX2FiaWxpdGllcyA8LSBzZWxlY3QoYWJpbGl0eV9saXN0LCBBYmlsaXR5LCBEZXNjcmlwdGlvbiwgIiAiKQ0KI2FiaWxpdHlfc3VtbWFyeQ0KDQojIyMjIyMjIyMjIyMjIyMjIw0KIyBTZWxlY3RlZCBQb2tlbW9uIE1vdmVzDQojIFNwbGl0IGxlYXJuZWQgbW92ZXMgaW50byB2ZWN0b3INCmVsZW1lbnRzIDwtIHVubGlzdChzdHJzcGxpdChwb2tlX2ZpbHRlciRNb3ZlcywgIiwiKSkNCiMgRXh0cmFjdCBsZXZlbHMgYW5kIG1vdmVzIChvZGQgdnMgZXZlbiBpbmRleGVkIHZhbHVlcykNCmxldmVscyA8LSBhcy5pbnRlZ2VyKGVsZW1lbnRzW2MoVFJVRSwgRkFMU0UpXSkgIyBPZGQtaW5kZXhlZCB2YWx1ZXMNCm1vdmVzIDwtIGVsZW1lbnRzW2MoRkFMU0UsIFRSVUUpXSAgICAgICAgICAgICAgIyBFdmVuLWluZGV4ZWQgdmFsdWVzDQoNCiMgQ3JlYXRlIHRoZSBkYXRhIGZyYW1lDQptb3Zlc19kZiA8LSBkYXRhLmZyYW1lKExldmVsID0gbGV2ZWxzLCBNb3ZlID0gbW92ZXMsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCnNlbGVjdGVkX3Bva2VfbW92ZXMgPC0gbGVmdF9qb2luKG1vdmVzX2RmLCBtb3ZlX2RiLCBieSA9IGMoIk1vdmUiID0gIk1vdmVfSUQiKSlbLDE6OF0NCg0KYGBgDQpgYGB7cn0NCiMgUG9rZW1vbiBTdW1tYXJ5DQojcG9rZV9maWx0ZXIgPC0gZmlsdGVyKHBva2VfZGIsIE5hbWUgPT0gIkdlb2R1ZGUiKQ0KcG9rZV9maWx0ZXIgPC0gcG9rZV9kYg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjDQojIyMjIEJhc2UgU3RhdHMgIyMjDQojIyMjIyMjIyMjIyMjIyMjIyMjDQpwb2tlX2ZpbHRlcl9iYXNlIDwtIHNlbGVjdChwb2tlX2ZpbHRlciwgUG9rZW1vbl9JRCwgTmFtZSwgRm9ybU5hbWUsIFR5cGVzLCBIUCwgQVRUQUNLLCBERUZFTlNFLCBTUEVDSUFMX0FUVEFDSywgU1BFQ0lBTF9ERUZFTlNFLCBTUEVFRCwgQWJpbGl0aWVzLCBIaWRkZW5BYmlsaXRpZXMsIEV2b2x1dGlvbnMsIE1vdmVzKQ0KDQpzZWxlY3RlZF9wb2tlX3N0YXRzIDwtIHBpdm90X2xvbmdlcihwb2tlX2ZpbHRlcl9iYXNlDQogICAgICAgICAgICAgLGNvbHMgPSBjKCJUeXBlcyIsIkhQIiwiQVRUQUNLIiwiREVGRU5TRSIsIlNQRUNJQUxfQVRUQUNLIiwiU1BFQ0lBTF9ERUZFTlNFIiwiU1BFRUQiLCJNb3ZlcyIsIkV2b2x1dGlvbnMiLCJBYmlsaXRpZXMiLCJIaWRkZW5BYmlsaXRpZXMiKQ0KICAgICAgICAgICAgICxuYW1lc190byA9ICJTdGF0Ig0KICAgICAgICAgICAgICx2YWx1ZXNfdG8gPSAiVmFsdWUiDQogICAgICAgICAgICAgKQ0KDQpzZWxlY3RlZF9wb2tlX3N0YXRzIDwtIGZpbHRlcihzZWxlY3RlZF9wb2tlX3N0YXRzLCEgU3RhdCAlaW4lIGMoIk1vdmVzIiwgIkFiaWxpdGllcyIsICJIaWRkZW5BYmlsaXRpZXMiLCAiRXZvbHV0aW9ucyIpKQ0KbmFtZXMoc2VsZWN0ZWRfcG9rZV9zdGF0cylbbmFtZXMoc2VsZWN0ZWRfcG9rZV9zdGF0cykgPT0gJ05hbWUnXSA8LSAnUG9rZW1vbicNCiNjb2xuYW1lcyhzZWxlY3RlZF9wb2tlX3N0YXRzKSA8LSBjKCJTdGF0IiwiVmFsdWUiKQ0KI3NlbGVjdGVkX3Bva2Vfc3RhdHMNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIyMgQWJpbGl0eSAjIyMjDQojIyMjIyMjIyMjIyMjIyMjIyMNCmFiaWxpdHlfZmlsdGVyIDwtIHNlbGVjdChwb2tlX2ZpbHRlcixQb2tlbW9uX0lELE5hbWUsRm9ybU5hbWUsQWJpbGl0aWVzLEhpZGRlbkFiaWxpdGllcykNCmFiaWxpdHlfZmlsdGVyIDwtIGFiaWxpdHlfZmlsdGVyICU+JSBzZXBhcmF0ZShBYmlsaXRpZXMsIGludG8gPSBjKCJBYmlsaXR5MSIsICJBYmlsaXR5MiIpLCBzZXAgPSAiLCIsIGZpbGwgPSAicmlnaHQiKQ0KYWJpbGl0eV9maWx0ZXIgPC0gYWJpbGl0eV9maWx0ZXIgJT4lIHNlcGFyYXRlKEhpZGRlbkFiaWxpdGllcywgaW50byA9IGMoIkhpZGRlbkFiaWxpdHkxIiwiSGlkZGVuQWJpbGl0eTIiLCJIaWRkZW5BYmlsaXR5MyIpLCBzZXAgPSAiLCIsIGZpbGwgPSAicmlnaHQiKQ0KI2FiaWxpdHlfZW5kIDwtIGdhdGhlcihhYmlsaXR5X2ZpbHRlciwga2V5PSJQb2tlbW9uX0lEIiwgdmFsdWUgPSAiQWJpbGl0eSIsIG5hLnJtID0gVFJVRSkNCg0KYWJpbGl0eV9lbmQgPC0gcGl2b3RfbG9uZ2VyKGFiaWxpdHlfZmlsdGVyDQogICAgICAgICAgICAgLGNvbHMgPSBjKCJBYmlsaXR5MSIsIkFiaWxpdHkyIiwiSGlkZGVuQWJpbGl0eTEiLCJIaWRkZW5BYmlsaXR5MiIsIkhpZGRlbkFiaWxpdHkzIikNCiAgICAgICAgICAgICAsbmFtZXNfdG8gPSAiVHlwZSINCiAgICAgICAgICAgICAsdmFsdWVzX3RvID0gIkFiaWxpdHkiKQ0KDQphYmlsaXR5X2xpc3QgPC0gZmlsdGVyKGxlZnRfam9pbihhYmlsaXR5X2VuZCwgYWJpbGl0eV9kYiwgYnkgPSBjKCJBYmlsaXR5IiA9ICJBYmlsaXR5X0lEIikpLCAhaXMubmEoQWJpbGl0eSkpDQpjb2xuYW1lcyhhYmlsaXR5X2xpc3QpIDwtIGMoIlBva2Vtb25fSUQiLCJQb2tlbW9uIiwiRm9ybU5hbWUiLCIgIiwiQWJpbGl0eSIsIk5hbWUiLCJEZXNjcmlwdGlvbiIsIkZsYWdzIikNCg0Kc2VsZWN0ZWRfcG9rZV9hYmlsaXRpZXMgPC0gc2VsZWN0KGFiaWxpdHlfbGlzdCwgUG9rZW1vbiwgQWJpbGl0eSwgRGVzY3JpcHRpb24sICIgIikNCiNhYmlsaXR5X3N1bW1hcnkNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIFNlbGVjdGVkIFBva2Vtb24gTW92ZXMgIyMjDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCnBva2VfZmlsdGVyX05BX1JNIDwtIGZpbHRlcihwb2tlX2ZpbHRlciwgIWlzLm5hKE1vdmVzKSkNCnBva2VfZmlsdGVyX21vdmVzIDwtIHNlbGVjdChwb2tlX2ZpbHRlcl9OQV9STSwgUG9rZW1vbl9JRCwgTmFtZSwgTW92ZXMpDQoNCiMgU3RlcCAxOiBTcGxpdCB0aGUgTW92ZXMgY29sdW1uIGludG8gc2VwYXJhdGUgcm93cyBiYXNlZCBvbiBjb21tYXMNCiMgVHJhbnNmb3JtIHRoZSBkYXRhDQpwb2tlX21vdmVfc3VtbWFyeSA8LSBwb2tlX2ZpbHRlcl9OQV9STSAlPiUNCiAgIyBTZXBhcmF0ZSBlYWNoIHZhbHVlIGluIHRoZSBNb3ZlcyBjb2x1bW4gaW50byBpbmRpdmlkdWFsIHJvd3MNCiAgc2VwYXJhdGVfcm93cyhNb3Zlcywgc2VwID0gIiwiKSAlPiUNCiAgIyBBZGQgYSBjb2x1bW4gd2l0aCByb3cgbnVtYmVycyB0byBkaXN0aW5ndWlzaCBiZXR3ZWVuIGxldmVsIGFuZCBtb3ZlIG5hbWVzDQogIGdyb3VwX2J5KE5hbWUpICU+JQ0KICBtdXRhdGUocm93X2lkID0gMiAqIGNlaWxpbmcocm93X251bWJlcigpIC8gMikpICU+JQ0KICAjIFVzZSBtb2R1bG8gdG8gaWRlbnRpZnkgb2RkIHJvd3MgYXMgbW92ZSBsZXZlbHMgYW5kIGV2ZW4gcm93cyBhcyBtb3ZlIG5hbWVzDQogIG11dGF0ZShjYXRlZ29yeSA9IGlmZWxzZShyb3dfbnVtYmVyKCkgJSUgMiA9PSAxLCAiTW92ZV9MZXZlbCIsICJNb3ZlX05hbWUiKSkgJT4lDQogICMgUGl2b3QgZGF0YSBzbyB0aGF0IE1vdmVfTGV2ZWwgYW5kIE1vdmVfTmFtZSBhcmUgaW4gc2VwYXJhdGUgY29sdW1ucw0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gY2F0ZWdvcnksIHZhbHVlc19mcm9tID0gTW92ZXMpICU+JQ0KICAjIFJlbW92ZSB0aGUgcm93X2lkIGNvbHVtbiBhbmQgdW5ncm91cA0KICBzZWxlY3QoLXJvd19pZCkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgIyBDb252ZXJ0IHRoZSBNb3ZlX0xldmVsIGNvbHVtbiB0byBudW1lcmljIChzaW5jZSBpdCdzIGNoYXJhY3RlciBhZnRlciBzcGxpdHRpbmcpDQogIG11dGF0ZShNb3ZlX0xldmVsID0gYXMuaW50ZWdlcihNb3ZlX0xldmVsKSkgJT4lDQogICMgU2VsZWN0IHJlbGV2YW50IGNvbHVtbnMNCiAgc2VsZWN0KFBva2Vtb25fSUQsIE5hbWUsIE1vdmVfTGV2ZWwsIE1vdmVfTmFtZSkNCg0KIyBSZW1vdmUgZHVwbGljYXRlcw0KcG9rZV9tb3ZlX3N1bW1hcnkgPC0gdW5pcXVlKHBva2VfbW92ZV9zdW1tYXJ5KQ0KDQpzZWxlY3RlZF9wb2tlX21vdmVzIDwtIGxlZnRfam9pbihwb2tlX21vdmVfc3VtbWFyeSwgbW92ZV9kYiwgYnkgPSBjKCJNb3ZlX05hbWUiID0gIk1vdmVfSUQiKSlbLDE6MTFdDQpjb2xuYW1lcyhzZWxlY3RlZF9wb2tlX21vdmVzKSA8LSBjKCJQb2tlbW9uX0lEIiwiUG9rZW1vbiIsIkxldmVsIiwiTW92ZV9JRCIsIk5hbWUiLCJUeXBlIiwiQ2F0ZWdvcnkiLCJQb3dlciIsIkFjY3VyYWN5IiwiUFAiLCJUYXJnZXQiKQ0KYGBgDQoNCmBgYHtyfQ0KZmlsdGVyKHNlbGVjdGVkX3Bva2VfbW92ZXMsIFBva2Vtb24gPT0gIlRzb3VraW5hdG9yIikNCg0KDQpmaWx0ZXIocG9rZV9tb3ZlX3N1bW1hcnksIE5hbWUgPT0gIkFib21hc25vdyIpDQpmaWx0ZXIocG9rZV9tb3ZlX3N1bW1hcnksIE5hbWUgPT0gIlRzb3VraW5hdG9yIikNCg0KZmlsdGVyKHBva2VfZmlsdGVyX05BX1JNLCBOYW1lID09ICJUc291a2luYXRvciIpDQpgYGANCg0KYGBge3J9DQp0ZXN0IDwtIGRhdGEuZnJhbWUoTmFtZSA9IHBva2VfZmlsdGVyX05BX1JNJE5hbWUsIE1vdmVzID0gcG9rZV9maWx0ZXJfTkFfUk0kTW92ZXMpDQoNCnRpZHlfbW92ZXMgPC0gdGVzdCAlPiUNCiAgIyBTZXBhcmF0ZSBlYWNoIHZhbHVlIGluIHRoZSBNb3ZlcyBjb2x1bW4gaW50byBpbmRpdmlkdWFsIHJvd3MNCiAgc2VwYXJhdGVfcm93cyhNb3Zlcywgc2VwID0gIiwiKSAlPiUNCiAgIyBBZGQgYSBjb2x1bW4gd2l0aCByb3cgbnVtYmVycyB0byBkaXN0aW5ndWlzaCBiZXR3ZWVuIGxldmVsIGFuZCBtb3ZlIG5hbWVzDQogIGdyb3VwX2J5KE5hbWUpICU+JQ0KICBtdXRhdGUocm93X2lkID0gMiAqIGNlaWxpbmcocm93X251bWJlcigpLzIpKSAlPiUNCiAgIyBVc2UgbW9kdWxvIHRvIGlkZW50aWZ5IHRoZSBvZGQgcm93cyBhcyBtb3ZlIGxldmVscyBhbmQgZXZlbiByb3dzIGFzIG1vdmUgbmFtZXMNCiAgbXV0YXRlKGNhdGVnb3J5ID0gaWZlbHNlKHJvd19pZCAlJSAyID09IDEsICJNb3ZlX0xldmVsIiwgIk1vdmVfTmFtZSIpKSAlPiUNCiAgIyBQaXZvdCBkYXRhIHNvIHRoYXQgTW92ZV9MZXZlbCBhbmQgTW92ZV9OYW1lIGFyZSBpbiBzZXBhcmF0ZSBjb2x1bW5zDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBjYXRlZ29yeSwgdmFsdWVzX2Zyb20gPSBNb3ZlcykgJT4lDQogICMgUmVtb3ZlIHRoZSByb3dfaWQgY29sdW1uIGFuZCB1bmdyb3VwDQogICNzZWxlY3QoLXJvd19pZCkgJT4lDQogIHVuZ3JvdXAoKQ0KDQp0c291ayA8LSBmaWx0ZXIodGlkeV9tb3ZlcywgTmFtZSA9PSAiVHNvdWtpbmF0b3IiKQ0KDQojMiAqIHJvdW5kKGRmJGEvMikNCnQgPC0gdW5saXN0KHRzb3VrJE1vdmVfTmFtZVtbMV1dKQ0KdA0KYGBgDQpgYGB7cn0NCiMgQXNzdW1pbmcgeW91J3ZlIGFscmVhZHkgdHJhbnNmb3JtZWQgdGhlIGRhdGEgYXMgZGVzY3JpYmVkDQp0aWR5X21vdmVzIDwtIHBva2VfZmlsdGVyX05BX1JNICU+JQ0KICAjIFNlcGFyYXRlIGVhY2ggdmFsdWUgaW4gdGhlIE1vdmVzIGNvbHVtbiBpbnRvIGluZGl2aWR1YWwgcm93cw0KICBzZXBhcmF0ZV9yb3dzKE1vdmVzLCBzZXAgPSAiLCIpICU+JQ0KICAjIEFkZCBhIGNvbHVtbiB3aXRoIHJvdyBudW1iZXJzIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gbGV2ZWwgYW5kIG1vdmUgbmFtZXMNCiAgZ3JvdXBfYnkoTmFtZSkgJT4lDQogIG11dGF0ZShyb3dfaWQgPSAyICogY2VpbGluZyhyb3dfbnVtYmVyKCkgLyAyKSkgJT4lDQogICMgVXNlIG1vZHVsbyB0byBpZGVudGlmeSBvZGQgcm93cyBhcyBtb3ZlIGxldmVscyBhbmQgZXZlbiByb3dzIGFzIG1vdmUgbmFtZXMNCiAgbXV0YXRlKGNhdGVnb3J5ID0gaWZlbHNlKHJvd19udW1iZXIoKSAlJSAyID09IDEsICJNb3ZlX0xldmVsIiwgIk1vdmVfTmFtZSIpKSAlPiUNCiAgIyBQaXZvdCBkYXRhIHNvIHRoYXQgTW92ZV9MZXZlbCBhbmQgTW92ZV9OYW1lIGFyZSBpbiBzZXBhcmF0ZSBjb2x1bW5zDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBjYXRlZ29yeSwgdmFsdWVzX2Zyb20gPSBNb3ZlcykgJT4lDQogICMgUmVtb3ZlIHRoZSByb3dfaWQgY29sdW1uIGFuZCB1bmdyb3VwDQogIHNlbGVjdCgtcm93X2lkKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICAjIENvbnZlcnQgdGhlIE1vdmVfTGV2ZWwgY29sdW1uIHRvIG51bWVyaWMgKHNpbmNlIGl0J3MgY2hhcmFjdGVyIGFmdGVyIHNwbGl0dGluZykNCiAgbXV0YXRlKE1vdmVfTGV2ZWwgPSBhcy5pbnRlZ2VyKE1vdmVfTGV2ZWwpKSAlPiUNCiAgIyBTZWxlY3QgcmVsZXZhbnQgY29sdW1ucw0KICBzZWxlY3QoUG9rZW1vbl9JRCwgTmFtZSwgTW92ZV9MZXZlbCwgTW92ZV9OYW1lKQ0KDQojIFZpZXcgdGhlIHJlc3VsdA0KcHJpbnQodGlkeV9tb3ZlcykNCg0KDQpmaWx0ZXIodGlkeV9tb3ZlcywgTmFtZSA9PSAiVHNvdWtpbmF0b3IiKQ0KYGBgDQpgYGB7cn0NCnRzb3VrbW9uX3BrbW4gPC0gc2VsZWN0KGZpbHRlcihyZWFkeGw6OnJlYWRfZXhjZWwoIlBva2VEQi54bHN4Iiwgc2hlZXQgPSAiUGttblN0YXRzIiksIGlzLm5hKGBQb2tlZGV4IE51bWJlcmApKSwgTmFtZSkNCnRzb3VrbW9uX3BrbW4kUG9rZW1vbl9JRCA8LSB0b3VwcGVyKGdzdWIoIiAiLCIiLHRzb3VrbW9uX3BrbW4kTmFtZSkpDQoNCnRzb3VrbW9uX21vdmVzIDwtIHNlbGVjdChmaWx0ZXIocmVhZHhsOjpyZWFkX2V4Y2VsKCJQb2tlREIueGxzeCIsIHNoZWV0ID0gIk1vdmVzIikpLCBOYW1lKQ0KdHNvdWttb25fbW92ZXMkTW92ZV9JRCA8LSB0b3VwcGVyKGdzdWIoIiAiLCIiLHRzb3VrbW9uX21vdmVzJE5hbWUpKQ0KDQp0c291a21vbl9hYmlsaXRpZXMgPC0gc2VsZWN0KGZpbHRlcihyZWFkeGw6OnJlYWRfZXhjZWwoIlBva2VEQi54bHN4Iiwgc2hlZXQgPSAiQWJpbGl0aWVzIikpLCBOYW1lKQ0KdHNvdWttb25fYWJpbGl0aWVzJEFiaWxpdHlfSUQgPC0gdG91cHBlcihnc3ViKCIgIiwiIix0c291a21vbl9hYmlsaXRpZXMkTmFtZSkpDQoNCnRlc3QgPC0gZmlsdGVyKHBva2VfZGJfc2hvcnQsIChOYW1lID09ICJUc291a2luYXRvciIgfCBOYW1lID09ICJQaWthY2h1IikpDQoNCnRlc3Rfam9pbiA8LSBsZWZ0X2pvaW4odGVzdCwgdHNvdWttb25fcGttbiwgYnkgPSBjKCJOYW1lIiA9ICJOYW1lIikpDQp0ZXN0X2pvaW4NCg0KdGVzdCRUc291a01vbl9PRyA8LSB0ZXN0JE5hbWUgJWluJSB0c291a21vbl9wa21uJE5hbWUNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQoNCnBhbGV0dGVfYmx1ZXMgPC0gY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPWMoIiNGMzQ0NDQiLCIjRkY3RjBGIiwiI0ZGREQ1NyIsIiNBMEU1MTUiLCIjMDBDMkI4IikpDQoNCnRzb3VrX2NvbG91cnMgPC0gYygiI0YzNDQ0NCIsIiNGRjdGMEYiLCIjRkZERDU3IiwiI0EwRTUxNSIsIiMwMEMyQjgiKQ0KdHNvdWtfcGFsZXR0ZSA8LSBicmV3ZXIucGFsKG49NSwgbmFtZT0iRGFyazIiKQ0KdHNvdWtfcGFsZXR0ZSA8LSB0c291a19wYWxldHRlDQojdmFsDQoNCmZvb2RiYWJ5IDwtIGZpbHRlcihzZWxlY3RlZF9wb2tlX3N0YXRzLCBQb2tlbW9uID09ICJGb29kQmFieSIgJiBTdGF0ICE9ICJUeXBlcyIpDQpmb29kYmFieSRTdGF0IDwtIGZhY3Rvcihmb29kYmFieSRTdGF0LCBsZXZlbHMgPSBjKCJIUCIsICJBVFRBQ0siLCAiREVGRU5TRSIsICJTUEVDSUFMX0FUVEFDSyIsICJTUEVDSUFMX0RFRkVOU0UiLCAiU1BFRUQiKSkNCg0KZm9vZGJhYnkkVmFsdWUgPC0gYXMubnVtZXJpYyhmb29kYmFieSRWYWx1ZSkNCmZpZyA8LSBwbG90X2x5KHggPSBmb29kYmFieSRWYWx1ZSwgeSA9IGZvb2RiYWJ5JFN0YXQsIHR5cGUgPSAnYmFyJywgb3JpZW50YXRpb24gPSAnaCcsDQogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gYnJld2VyLnBhbCg2LCAiRGFyazIiKSksDQogICAgICAgICAgICAgICB0ZXh0ID0gZm9vZGJhYnkkVmFsdWUsICAgICAgICMgU2hvdyB0aGUgVmFsdWUgYXMgdGV4dA0KICAgICAgICAgICAgICAgdGV4dHBvc2l0aW9uID0gJ2F1dG8nKSAlPiUNCiAgbGF5b3V0KA0KICAgIHhheGlzID0gbGlzdChyYW5nZSA9IGMoMCwgMjAwKSxzaG93dGlja2xhYmVscyA9IEZBTFNFKSwNCiAgICB5YXhpcyA9IGxpc3QoYXV0b3JhbmdlID0gInJldmVyc2VkIg0KICAgICksDQogICAgaGVpZ2h0ID0gMzAwDQogICkNCg0KZmlnDQoNCg0KDQojYnJld2VyLnBhbCg2LCAiRGFyazIiKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShwbG90bHkpDQoNCiMgWW91ciBjdXN0b20gY29sb3IgcGFsZXR0ZQ0KdHNvdWtfY29sb3VycyA8LSBjKCIjRjM0NDQ0IiwgIiNGRjdGMEYiLCAiI0ZGREQ1NyIsICIjQTBFNTE1IiwgIiMwMEMyQjgiKQ0KDQojIERlZmluZSBicmVha3MgYW5kIGNvcnJlc3BvbmRpbmcgY29sb3JzDQpicmVha3MgPC0gYygtMSwgNDAsIDYwLCA5MCwgMTIwLCAyMDApDQpjb2xvcnMgPC0gdHNvdWtfY29sb3Vycw0KDQojIEZ1bmN0aW9uIHRvIGFzc2lnbiBjb2xvcnMgYmFzZWQgb24gYnJlYWtzDQphc3NpZ25fY29sb3IgPC0gZnVuY3Rpb24odmFsdWUpIHsNCiAgY3V0KHZhbHVlLCBicmVha3MgPSBicmVha3MsIGxhYmVscyA9IGNvbG9ycywgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKQ0KfQ0KDQojIEFwcGx5IHRoZSBjb2xvciBhc3NpZ25tZW50IGZ1bmN0aW9uDQp2YWx1ZV9jb2xvcnMgPC0gYXNzaWduX2NvbG9yKGZvb2RiYWJ5JFZhbHVlKQ0KDQojIENyZWF0ZSB0aGUgUGxvdGx5IGZpZ3VyZQ0KZmlnIDwtIHBsb3RfbHkoeCA9IGZvb2RiYWJ5JFZhbHVlLCB5ID0gZm9vZGJhYnkkU3RhdCwgdHlwZSA9ICdiYXInLCBvcmllbnRhdGlvbiA9ICdoJywNCiAgICAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSB2YWx1ZV9jb2xvcnMpLCAjIFVzZSBkaXNjcmV0ZSBjb2xvcnMNCiAgICAgICAgICAgICAgIHRleHQgPSBmb29kYmFieSRWYWx1ZSwgICAgICAgIyBTaG93IHRoZSBWYWx1ZSBhcyB0ZXh0DQogICAgICAgICAgICAgICB0ZXh0cG9zaXRpb24gPSAnYXV0bycpICU+JQ0KICBsYXlvdXQoDQogICAgeGF4aXMgPSBsaXN0KHJhbmdlID0gYygwLCAyMDApLCBzaG93dGlja2xhYmVscyA9IEZBTFNFKSwNCiAgICB5YXhpcyA9IGxpc3QoYXV0b3JhbmdlID0gInJldmVyc2VkIiksDQogICAgaGVpZ2h0ID0gMzAwDQogICkNCg0KZmlnDQoNCg0KYGBgDQoNCmBgYHtyfQ0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJ5DQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoZ3JEZXZpY2VzKQ0KDQojIE9yaWdpbmFsIGNvbG9yIHBhbGV0dGUNCnRzb3VrX2NvbG91cnMgPC0gYygiI0YzNDQ0NCIsICIjRkY3RjBGIiwgIiNGRkRENTciLCAiI0EwRTUxNSIsICIjMDBDMkI4IikNCg0KIyBGdW5jdGlvbiB0byBjcmVhdGUgYSBjb2xvciBzY2FsZSB3aXRoIGludGVybWVkaWF0ZSBjb2xvcnMNCmNyZWF0ZV9jb2xvcl9zY2FsZSA8LSBmdW5jdGlvbihjb2xvcnMsIG4pIHsNCiAgIyBJbnRlcnBvbGF0ZSBjb2xvcnMNCiAgY29sb3Jfc2NhbGUgPC0gY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMpKG4pDQogIHJldHVybihjb2xvcl9zY2FsZSkNCn0NCg0KIyBDcmVhdGUgYSBuZXcgY29sb3Igc2NhbGUgd2l0aCAxMCBjb2xvcnMNCmV4cGFuZGVkX2NvbG91cnMgPC0gY3JlYXRlX2NvbG9yX3NjYWxlKHRzb3VrX2NvbG91cnMsIDgpDQoNCiMgRGVmaW5lIGJyZWFrcyAob25lIG1vcmUgdGhhbiB0aGUgbnVtYmVyIG9mIGNvbG9ycykNCmJyZWFrcyA8LSBjKC0xLCAwLCAyMCwgNDAsIDYwLCA4MCwgMTAwLCAxNTAsIDIwMCkNCmNvbG9ycyA8LSBleHBhbmRlZF9jb2xvdXJzICAjIFVzZSB0aGUgZXhwYW5kZWQgY29sb3IgcGFsZXR0ZQ0KDQojIEZ1bmN0aW9uIHRvIGFzc2lnbiBjb2xvcnMgYmFzZWQgb24gYnJlYWtzDQphc3NpZ25fY29sb3IgPC0gZnVuY3Rpb24odmFsdWUpIHsNCiAgY3V0KHZhbHVlLCBicmVha3MgPSBicmVha3MsIGxhYmVscyA9IGNvbG9ycywgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KDQpiYXNlX3N0YXRzIDwtIGZpbHRlcihzZWxlY3RlZF9wb2tlX3N0YXRzLCBQb2tlbW9uID09ICJIYXlkb3MiICYgU3RhdCAhPSAiVHlwZXMiKQ0KYmFzZV9zdGF0cyRTdGF0IDwtIGZhY3RvcihiYXNlX3N0YXRzJFN0YXQsIGxldmVscyA9IGMoIkhQIiwgIkFUVEFDSyIsICJERUZFTlNFIiwgIlNQRUNJQUxfQVRUQUNLIiwgIlNQRUNJQUxfREVGRU5TRSIsICJTUEVFRCIpKQ0KDQpiYXNlX3N0YXRzJFZhbHVlIDwtIGFzLm51bWVyaWMoYmFzZV9zdGF0cyRWYWx1ZSkNCg0KIyBBcHBseSB0aGUgY29sb3IgYXNzaWdubWVudCBmdW5jdGlvbg0KdmFsdWVfY29sb3JzIDwtIGFzc2lnbl9jb2xvcihiYXNlX3N0YXRzJFZhbHVlKQ0KDQojIENyZWF0ZSB0aGUgUGxvdGx5IGZpZ3VyZQ0KZmlnIDwtIHBsb3RfbHkoeCA9IGJhc2Vfc3RhdHMkVmFsdWUsIHkgPSBiYXNlX3N0YXRzJFN0YXQsIHR5cGUgPSAnYmFyJywgb3JpZW50YXRpb24gPSAnaCcsDQogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gdmFsdWVfY29sb3JzKSwgIyBVc2UgZGlzY3JldGUgY29sb3JzDQogICAgICAgICAgICAgICB0ZXh0ID0gYmFzZV9zdGF0cyRWYWx1ZSwgICAgICAgIyBTaG93IHRoZSBWYWx1ZSBhcyB0ZXh0DQogICAgICAgICAgICAgICB0ZXh0cG9zaXRpb24gPSAnYXV0bycpICU+JQ0KICBsYXlvdXQoDQogICAgeGF4aXMgPSBsaXN0KHJhbmdlID0gYygwLCAyMDApLCBzaG93dGlja2xhYmVscyA9IEZBTFNFLCBzaG93Z3JpZCA9IEZBTFNFKSwNCiAgICB5YXhpcyA9IGxpc3QoYXV0b3JhbmdlID0gInJldmVyc2VkIiksDQogICAgaGVpZ2h0ID0gMjcwLA0KICAgIHBsb3RfYmdjb2xvciAgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgICBwYXBlcl9iZ2NvbG9yID0gInRyYW5zcGFyZW50Ig0KICApDQoNCmZpZw0KDQpgYGANCg0K